package CPU.rv64_1stage
import chisel3._
import chisel3.util._
import chisel3.util.experimental.BoringUtils
import difftest._
class Core extends Module with HasInstrType {
  val io = IO(new Bundle{
    val imem = new RomIO
    val dmem = new RamIO
  })
  val imm_b ,imm_j = WireInit(0.U(32.W))
  val imm_i,imm_u,imm_s = WireInit(0.U(64.W))
  val rd_data = WireInit(0.U(64.W))

  val fetch = Module(new InstFetch)
  val decode = Module(new Decode).io
  val exec_out = Module(new Execution).io
  val rf_out = RegFile(decode.rs1_addr,decode.rs2_addr,decode.rd_addr,rd_data,decode.rd_en)
  // instr fetch
  val hit   = exec_out.branhit
  io.imem <> fetch.io.imem
  fetch.io.isbran := (decode.fuType === FuType.bru) && hit

  val bran_offset = MuxCase(0.U,Array(
    (decode.instType === InstrB) -> imm_b,
    (decode.instType === InstrJ) -> imm_j,
  ))
  val jalrpc = Cat(exec_out.out(31,1),0.U(1.W))
  val isjalr = (decode.opcode===ALUOpType.jalr)&&(decode.instType===InstrI)
  val pc4    = fetch.io.pc + 4.U
  fetch.io.branpc := Mux(isjalr,jalrpc,fetch.io.pc + bran_offset)
  // decode
  decode.inst := fetch.io.inst
  rd_data := Mux(isjalr,ZeroExt(pc4,64),exec_out.out)

  val inst = fetch.io.inst
  imm_i      := Cat(Fill(53,inst(31)),inst(30,20))
  imm_u      := Cat(Fill(33,inst(31)),inst(30,12),0.U(12.W))
  imm_s      := Cat(Fill(53,inst(31)),inst(30,25),inst(11,8),inst(7))
  imm_b      := Cat(Fill(20,inst(31)),inst(7),inst(30,25),inst(11,8),0.U(1.W))
  imm_j      := Cat(Fill(12,inst(31)),inst(19,12),inst(20),inst(30,21),0.U(1.W))


  val in1 = MuxCase(0.U ,Array(
    (decode.rs1_op === 1.U ) -> rf_out._1,
    (decode.rs1_op === 2.U ) -> fetch.io.pc,
  ))
  val in2 = MuxCase(0.U,Array(
    (decode.rs2_op === 1.U) -> rf_out._2,
    (decode.rs2_op === 2.U) -> imm_i,
    (decode.rs2_op === 3.U) -> imm_u,
    (decode.rs2_op === 4.U) -> 4.U,
    (decode.rs2_op === 5.U) -> imm_s,
  ))
  // exec
  exec_out.in1 := in1
  exec_out.in2 := in2
  exec_out.opcode := decode.opcode
  exec_out.fuType := decode.fuType
  exec_out.rs2    := rf_out._2
  io.dmem <> exec_out.dmem

//  printf("%x %x %x %x\n",inst,in1, in2,rd_data)

  ////////////////////////////////////////////
  /* ----- Difftest ------------------------------ */
  val pc_valid = WireInit(false.B)
  BoringUtils.addSink(pc_valid, "pc_valid")
  val dt_ic = Module(new DifftestInstrCommit)
  dt_ic.io.clock    := clock
  dt_ic.io.coreid   := 0.U
  dt_ic.io.index    := 0.U
  dt_ic.io.valid    := RegNext(pc_valid)//true.B
  dt_ic.io.pc       := RegNext(fetch.io.pc)
  dt_ic.io.instr    := RegNext(fetch.io.inst)
  dt_ic.io.skip     := false.B
  dt_ic.io.isRVC    := false.B
  dt_ic.io.fpwen    := false.B
  dt_ic.io.rfwen    := RegNext(decode.rd_en)
  dt_ic.io.wpdest   := DontCare
  dt_ic.io.wdest    := RegNext(decode.rd_addr)

  val difftest_wb = Module(new DifftestIntWriteback)
  difftest_wb.io.clock := clock
  difftest_wb.io.coreid := 0.U
  difftest_wb.io.valid := true.B
  difftest_wb.io.dest := RegNext(decode.rd_addr)
  difftest_wb.io.data := RegNext(rd_data)

  val cycle_cnt = RegInit(0.U(64.W))
  val instr_cnt = RegInit(0.U(64.W))

  cycle_cnt := cycle_cnt + 1.U
  instr_cnt := instr_cnt + 1.U

  val rf_a0 = WireInit(0.U(64.W))
  BoringUtils.addSink(rf_a0, "rf_a0")

  val dt_te = Module(new DifftestTrapEvent)
  dt_te.io.clock    := clock
  dt_te.io.coreid   := 0.U
  dt_te.io.valid    := RegNext(fetch.io.inst === "h0000006b".U)
  dt_te.io.code     := RegNext(rf_a0(2, 0))
  dt_te.io.pc       := RegNext(fetch.io.pc)
  dt_te.io.cycleCnt := RegNext(cycle_cnt)
  dt_te.io.instrCnt := RegNext(instr_cnt)

  val dt_cs = Module(new DifftestCSRState)
  dt_cs.io.clock          := clock
  dt_cs.io.coreid         := 0.U
  dt_cs.io.priviledgeMode := 3.U  // Machine mode
  dt_cs.io.mstatus        := 0.U
  dt_cs.io.sstatus        := 0.U
  dt_cs.io.mepc           := 0.U
  dt_cs.io.sepc           := 0.U
  dt_cs.io.mtval          := 0.U
  dt_cs.io.stval          := 0.U
  dt_cs.io.mtvec          := 0.U
  dt_cs.io.stvec          := 0.U
  dt_cs.io.mcause         := 0.U
  dt_cs.io.scause         := 0.U
  dt_cs.io.satp           := 0.U
  dt_cs.io.mip            := 0.U
  dt_cs.io.mie            := 0.U
  dt_cs.io.mscratch       := 0.U
  dt_cs.io.sscratch       := 0.U
  dt_cs.io.mideleg        := 0.U
  dt_cs.io.medeleg        := 0.U

  val difftestArchEvent = Module(new DifftestArchEvent)
  difftestArchEvent.io.clock := clock
  difftestArchEvent.io.coreid := 0.U // TODO
  difftestArchEvent.io.intrNO := DontCare
  difftestArchEvent.io.cause := DontCare
  difftestArchEvent.io.exceptionPC := DontCare
  difftestArchEvent.io.exceptionInst := DontCare


}
